﻿package com.AUI.components
{
	import com.AUI.*;
	import com.AUI.define.*;
	
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
	import flash.utils.*;
	/**
	 * ScrollBar.
	 * @author HuangMinPing
	 * @site www.AminLab.com
	 */
	public class ScrollBar extends AComponent
	{
		/**
		 * The upper limit of scroll pixels.
		 */
		public static const SCROLL_PIXELS_UPPER_LIMIT:int = 0x80000000;
		/**
		 * The lower limit of scroll pixels. 
		 */
		public static const SCROLL_PIXELS_LOWER_LIMIT:int = 0x7FFFFFFF;
		/**
		 * [element] move arrow button #0.
		 */
		public var e_arrow0:Button;
		/**
		 * [element] move arrow button #1
		 */
		public var e_arrow1:Button;
		/**
		 * [element] track button.
		 */
		public var e_track:Button;
		/**
		 * [element] thumb button.
		 */
		public var e_thumb:Button;
		/**
		 * Setting of scrollbar.
		 * @see #scrollByThumb()
		 * @see #scrollByArrow()
		 * @see #scrollByWheel()
		 */
		public var setting:AScrollBarSetting = new AScrollBarSetting;
		/**
		 * Configuration.
		 */
		private var _config:CONFIG = new CONFIG;
		/**
		 * Thumb.
		 */
		private var _thumb:THUMB = new THUMB;
		/**
		 * Moving.
		 */
		private var _moving:MOVING = new MOVING;
		/**
		 * Show rectangle of attach.
		 */
		private var _showRect:Object;
		/**
		 * Full rectangle of attach.
		 */
		private var _fullRect:Object;
		/**
		 * The percent of current scroll position.
		 */
		private var _value:Number = 0;
		/**
		 * Constructor.
		 * @param vertical Indicates the scrollbar is horizontal or vertical scrolling.
		 */
		public function ScrollBar(horizontal:Boolean = true)
		{
			// init attributes
			depth = 2;
			_showRect = AUIPrivate.SHOWRECT;
			_fullRect = AUIPrivate.FULLRECT;
			if(horizontal)
			{
				_config.X = 'x';
				_config.W = 'width';
			}
			else
			{
				_config.X = 'y';
				_config.W = 'height';
			}
			// init events
			addEventListener(MouseEvent.MOUSE_WHEEL,onMouseWheel);
			
			/**************** INITCOMPLETE ****************/
			Object(this).constructor == ScrollBar && initComplete();
		}
		/**
		 * Pur order all elements.
		 * @private
		 */
		override protected function initComplete():void
		{
			// initialize
			var X:String = _config.X;
			var W:String = _config.W;
			var AB0:Button = new Button;
			var AB1:Button = new Button;
			var TRB:Button = new Button;
			var THB:Button = new Button;
			AB0.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDownButton,false,0,true);
			AB1.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDownButton,false,0,true);
			TRB.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDownButton,false,0,true);
			THB.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDownButton,false,0,true);
			// adjust elements' position
			TRB[X] = AB0[W];
			THB[X] = AB0[W];
			AB1[X] = AB0[W] + TRB[W];
			// add elements
			elements.addChild(AB0);
			elements.addChild(AB1);
			elements.addChild(TRB);
			elements.addChild(THB);
			// save reference
			e_arrow0 = AB0;
			e_arrow1 = AB1;
			e_track  = TRB;
			e_thumb  = THB;
			// super do
			super.initComplete();
		}
		/**
		 * Get the percent of current scroll position.
		 */
		override public function get value():*
		{
			return _value;
		}
		/**
		 * Called when MouseEvent.MOUSE_WHEEL event has been dispatched on component.
		 */
		protected function onMouseWheel(e:MouseEvent):void
		{
			scrollWheel(e.delta);
			e.stopPropagation();
		}
		/**
		 * @private
		 */
		override public function set disabled(value:Boolean):void
		{
			e_thumb.disabled  =
			e_track.disabled  = 
			e_arrow1.disabled =
			e_arrow0.disabled =
			super.disabled    = value;
		}
		/**
		 * Attach to a show window and it correlative whole display object,and then scrollbar will take over scroll of target.
		 * @param window The show window that may be a rectangle or DisplayObject,promiss one point: the object must be has width,height 2 attributes to work.
		 * @param whole The whole display object that correlative with show window.
		 */
		public function attach(show_rect:Object,full_rect:DisplayObject):void
		{
			_showRect = show_rect;
			_fullRect = full_rect;
		}
		/**
		 * Detach correlative window and whole display object.
		 */
		public function detach():void
		{
			_showRect = null;
			_fullRect = null;
		}
		/**
		 * @private
		 */
		override protected function onClick(e:MouseEvent):void
		{
			super.onClick(e);
			e.stopPropagation();
		}
		/**
		 * Called when MouseEvent.CLICK event has been dispatched on the button of component.
		 */
		protected function onMouseDownButton(e:MouseEvent):void
		{
			var X:String  = _config.X;
			var W:String  = _config.W;
			var SX:String = 'stage'+ X.toLocaleUpperCase();
			// get moving value
			var button:Button = Button(e.currentTarget);
			switch(button)
			{
				// thumb button
				case e_thumb:
					_moving.func	= scrollThumb;
					_moving.lastX	= e[SX];
					e_thumb.addEventListener(MouseEvent.ROLL_OUT,onRollOutThumb,false,1,true);
					stage.addEventListener(MouseEvent.MOUSE_MOVE,onStageMoving,false,0,true);
					stage.addEventListener(MouseEvent.MOUSE_UP,onStageCancelMoving,false,0,true);
					return;
				
				// arrow #0
				case e_arrow0:
					_moving.func   = scroll;
					_moving.offset = -setting.scroll_steps;
					break;
				// arrow #1
				case e_arrow1:
					_moving.func   = scroll;
					_moving.offset = setting.scroll_steps;
					break;
				// track
				case e_track:
					_moving.func   = scrollThumb;
					var THW:int    = e_thumb[W];
					var THP:Point  = e_thumb.localToGlobal(new Point(0,0));
					_moving.offset = e[SX] < THP[X] ? -THW : THW;
					break;
				default:
					throw new Error('unkown scrollbar button');
			}
			// listen events
			_moving.delay = setting.scroll_delay_first;
			onMoving();
			_moving.delay = setting.scroll_delay;
			button.addEventListener(MouseEvent.ROLL_OUT,onCancelMoving,false,0,true);
			button.addEventListener(MouseEvent.MOUSE_UP,onCancelMoving,false,0,true);
			// cancel events
			e.stopImmediatePropagation();
			e.stopPropagation();
			e.preventDefault();
		}
		/**
		 * Scroll by offset value as behavior of arrow button.
		 * @see #setting
		 */
		public function scrollWheel(delta:int):void
		{
			scroll(-delta * setting.scroll_steps);
		}
		/**
		 * Scroll by offset pixels value as behavior of arrow button.
		 * @see #setting
		 */
		public function scroll(pixels:int):void
		{
			var X:String = _config.X;
			var W:String = _config.W;
			// get parameters
			var SW:Number = _showRect[W];
			var FW:Number = _fullRect[W];
			var TRB:Button = e_track;
			var TRW:Number  = TRB[W];
			var TRX:Number  = TRB[X];
			var THB:Button = e_thumb;
			var THW:Number  = THB[W];
			// check position
			var nextX:Number = _fullRect[X] - pixels;
			var endX:Number  = -(FW - SW);
			if(nextX >= 0)
			{
				_value		 = 0;
				THB[X]		 = TRX;
				_fullRect[X] = 0;
			}
			else if(nextX <= endX)
			{
				_value		 = 1;
				THB[X]       = TRX + TRW - THW;
				_fullRect[X] = endX;
			}
			else
			{
				_value		 = -nextX / FW;
				THB[X]		 = TRX + int(_value * _thumb.move_scale * TRW);
				_fullRect[X] = nextX;
			}
		}
		/**
		 * Scroll by offset value as behavior of thumb button.
		 * @see #thumbPosition
		 * @see #setting
		 */
		public function scrollThumb(offset:int):void
		{
			var X:String = _config.X;
			var W:String = _config.W;
			// get parameters
			var SW:Number = e_thumb[W];
			var FW:Number = e_track[W];
			var TRB:Button = e_track;
			var TRW:Number  = TRB[W];
			var TRX:Number  = TRB[X];
			var THB:Button = e_thumb;
			var THW:Number  = THB[W]; 
			var THX:Number  = THB[X]; 
			// check position
			var nextX:Number = THX - TRX + offset;
			if(nextX <= 0)
			{
				_value		 = 0;
				THB[X]		 = TRX;
				_fullRect[X] = 0;
			}
			else if(nextX + SW >= FW)
			{
				_value		 = 1;
				THB[X]		 = TRX + TRW - THW;
				_fullRect[X] = -(_fullRect[W] - _showRect[W]);
			}
			else
			{
				_value		 = nextX / FW;
				THB[X]		 = TRX + nextX;
				_fullRect[X] = -int(_value / _thumb.move_scale * _fullRect[W]);
			}
		}
		/**
		 * @private
		 */
		private function onMoving():void
		{
			_moving.func(_moving.offset);
			_moving.delay *= setting.scroll_delay_decay;
			_moving.tid = 0;
			_moving.tid = setTimeout(onMoving,_moving.delay);
		}
		/**
		 * @private
		 */
		private function onCancelMoving(e:Event):void
		{
			e.currentTarget.removeEventListener(MouseEvent.ROLL_OUT,onCancelMoving);
			e.currentTarget.removeEventListener(MouseEvent.MOUSE_UP,onCancelMoving);
			clearInterval(_moving.tid);
			_moving.tid    = 0;
			_moving.func   = null;
			_moving.offset = 0;
			_moving.delay  = 0;
		}
		/**
		 * @private
		 */
		private function onStageMoving(e:MouseEvent):void
		{
			var X:String = _config.X;
			var SX:String = 'stage'+ X.toLocaleUpperCase();
			var nowX:int = e[SX];
			scrollThumb(nowX - _moving.lastX);
			_moving.lastX = nowX;
		}
		/**
		 * @private
		 */
		private function onStageCancelMoving(e:MouseEvent):void
		{
			e_thumb.removeEventListener(MouseEvent.ROLL_OUT,onRollOutThumb);
			stage.removeEventListener(MouseEvent.MOUSE_MOVE,onStageMoving);
			stage.removeEventListener(MouseEvent.MOUSE_UP,onStageCancelMoving);
			if(e.target != e_thumb)
			{
				e_thumb.dispatchEvent(new MouseEvent(MouseEvent.ROLL_OUT));
			}
		}
		/**
		 * @private
		 */
		private function onRollOutThumb(e:MouseEvent):void
		{
			if(_moving.func == scrollThumb)
			{
				e.stopImmediatePropagation();
			}
		}
		/**
		 * @private
		 */
		override protected function redrawBoxModel():void
		{
			// 1.super do
			super.redrawBoxModel();
			// 2.initialize
			var X:String  = _config.X;
			var W:String  = _config.W;
			var AB0:Button = e_arrow0;
			var AB1:Button = e_arrow1;
			var TRB:Button = e_track;
			var ABW0:int = AB0[W];
			var ABW1:int = AB1[W];
			var TRW:int  = boxModel.content[W] - ABW0 - ABW1;
			if(TRW < 0)
			{
				TRW = 0;
			}
			// 3.update attribute
			TRB[W] = TRW;
			TRB[X] = ABW0;
			AB1[X] = ABW0 + TRW;
			// 4.adjust thumb button
			readjustThumb();
		}
		/**
		 * Call it to refresh thumb button.
		 */
		public function readjustThumb():void
		{
			var W:String = _config.W;
			var SW:Number = _showRect[W];
			var FW:Number = _fullRect[W];
			var scale:Number = SW / FW;
			// 1.update thumb size scale
			if(scale > 1 || scale == Infinity || isNaN(scale))
			{
				return;
			}
			_thumb.size_scale = scale;
			// 2.update thumb move scale
			var TRW:int = e_track[W];
			var THW:int = int(TRW * _thumb.size_scale);
			if(THW < setting.min_thumb_size)
			{
				_thumb.move_scale = (TRW - setting.min_thumb_size) / (TRW - THW);
				THW = setting.min_thumb_size;
			}
			else
			{
				_thumb.move_scale = 1;
			}
			// 3.update thumb
			e_thumb[W] = THW;
			scroll(0);
		}
	}
}
/**
 * Config.
 */
class CONFIG
{
	/**
	 * Coordinate X.
	 */
	public var X:String;
	/**
	 * Width.
	 */
	public var W:String
}
/**
 * Moving.
 */
class MOVING
{
	/**
	 * Timer id.
	 */
	public var tid:int;
	/**
	 * Current scroll function.
	 */
	public var func:Function;
	/**
	 * Current scroll offset.
	 */
	public var offset:int = 0;
	/**
	 * Delay.
	 */
	public var delay:int = 0;
	/**
	 * Last point of mouse.
	 */
	public var lastX:int  = 0;
}
/**
 * Thumb.
 */
class THUMB
{
	/**
	 * Thumb scale.
	 */
	public var size_scale:Number = 1;
	/**
	 * Thumb scale.
	 */
	public var move_scale:Number = 1;
}